package com.bitbakery.plugin.cl.lang.parser;

import com.bitbakery.plugin.cl.CLConstants;
import com.bitbakery.plugin.cl.lang.lexer.CLTokenTypes;
import com.bitbakery.plugin.cl.lang.psi.CLElementTypes;
import com.intellij.lang.PsiBuilder;
import com.intellij.psi.tree.IElementType;

public class CLClassParser {
    /**
     * Input: Builder is pointing at a "defclass" token
     * Output: Builder is pointing at...?
     */
    public static void parseClassDefinition(PsiBuilder builder) {
        PsiBuilder.Marker functionDefinitionMarker = builder.mark();

        builder.advanceLexer();

        parseClassName(builder);
        parseFormalParameterList(builder);
        parseDocstring(builder);
        parseFunctionBody(builder);

        builder.advanceLexer();

        functionDefinitionMarker.done(CLElementTypes.FUNCTION_DEFINITION);
    }

    protected static void parseClassName(PsiBuilder builder) {
        IElementType nextToken = builder.getTokenType();

        PsiBuilder.Marker functionNameMarker = builder.mark();
        builder.advanceLexer();

        if (nextToken == CLTokenTypes.LEFT_PAREN) {
            //parseFunctionCall(builder);
        } else if (nextToken != CLTokenTypes.SYMBOL) {
            builder.error(CLConstants.ERROR_EXPECTED_SYMBOL);
            functionNameMarker.drop();
            return;
        }
        functionNameMarker.done(CLElementTypes.FUNCTION_NAME);
    }

    /**
     * Input: PsiBuilder is pointing at the first left paren enclosing the formal parameter list
     * Output: PsiBuilder is pointing at...?
     */
    protected static void parseFormalParameterList(PsiBuilder builder) {
        IElementType nextToken = builder.getTokenType();
        if (nextToken != CLTokenTypes.LEFT_PAREN) {
            builder.error(CLConstants.ERROR_EXPECTED_LEFT_PAREN);
        } else {
            builder.advanceLexer();
        }

        PsiBuilder.Marker functionParameterListMarker = builder.mark();

        parseRegularParameterList(builder);
        //parseOptionalParameterList(builder);
        //parseKeywordParameterList(builder);
        //parseRestParameterList(builder);

        functionParameterListMarker.done(CLElementTypes.FORMAL_PARAMETER_LIST);
        builder.advanceLexer();
    }

    /**
     * Input: PsiBuilder must be pointed at the first regular parameter (if any).
     * Output: PsiBuilder is pointed at either:
     * (a) Closing right paren of the formal paramter list
     * (b) The &optional token
     * (c) The &key token
     * (d) The &rest token
     */
    private static void parseRegularParameterList(PsiBuilder builder) {
        PsiBuilder.Marker regularParameterListMarker = builder.mark();

        do {
            PsiBuilder.Marker regularParameterMarker = builder.mark();
            builder.advanceLexer();
            regularParameterMarker.done(CLElementTypes.REGULAR_PARAMETER);
        } while (builder.getTokenType() != CLTokenTypes.RIGHT_PAREN);

        regularParameterListMarker.done(CLElementTypes.REGULAR_PARAMETER_LIST);
    }

    protected static void parseDocstring(PsiBuilder builder) {
        if (builder.getTokenType() == CLTokenTypes.STRING_LITERAL) {
            PsiBuilder.Marker docstringMarker = builder.mark();
            builder.advanceLexer();
            docstringMarker.done(CLElementTypes.DOCSTRING);
        }
    }

    public static void parseFunctionBody(PsiBuilder builder) {
        PsiBuilder.Marker functionBodyMarker = builder.mark();
        while (builder.getTokenType() != CLTokenTypes.RIGHT_PAREN) {
            CLParser.parseStatement(builder);
        }
        functionBodyMarker.done(CLElementTypes.FUNCTION_BODY);
    }
}
